﻿#include "chinese.h"
/*! ************************************************************
 * \file
 * \brief 时间段的数据封装
 *
 * \authors 林胜勇
 *
 * \date
 * \verbatim
 * 变更记录：
 * 2023-11-30，林胜勇:
 *   创建文件
 * \endverbatim
 ****************************************************************/
#pragma once

#include <string>
#include <time.h>

#define TM_SINCE_YEAR 1900  // TM表示日期开始年
#define UTC_SINCE_YEAR 1970 // UTC开始年

#define SUMMERTIME_BEGIN_MONTH 3 // 夏令时开始月
#define SUMMERTIME_END_MONTH 10  // 夏令时结束月

#define MIN_MONTH 1  // 最小月份值
#define MAX_MONTH 12 // 最大月份值

#define MIN_DAY 1  // 每月最少日值，即1号
#define MAX_DAY 31 // 每月最大天数，不同月份不同

#define MIN_WEEKDAY 1 // 每周第1天
#define MAX_WEEKDAY 7 // 每周天数

#define MIN_HOUR 0  // 表示小时的最小值
#define MAX_HOUR 24 // 表示小时的最大值

#define MIN_MINUTE 0  // 表示分钟的最小值
#define MAX_MINUTE 60 // 表示分钟的最大值

#define MIN_SECOND 0  // 表示秒的最小值
#define MAX_SECOND 60 // 表示秒的最大值

#define MIN_MILLISECOND 0    // 毫秒最小值
#define MAX_MILLISECOND 1000 // 毫秒最大值

#define FACTOR_SEC_TO_MILLI 1000                  // 秒转换毫秒倍率
#define FACTOR_MIN_TO_MILLI (60 * 1000)           // 分钟转毫秒倍率
#define FACTOR_MIN_TO_SEC 60                      // 分钟转秒倍率
#define FACTOR_HOUR_TO_MILLI (60 * 60 * 1000)     // 小时转毫秒倍率
#define FACTOR_HOUR_TO_SEC (60 * 60)              // 小时转秒倍率
#define FACTOR_HOUR_TO_MIN 60                     // 小时转分钟倍率
#define FACTOR_DAY_TO_MILLI (24 * 60 * 60 * 1000) // 天转毫秒倍率
#define FACTOR_DAY_TO_SEC (24 * 60 * 60)          // 天转秒倍率
#define FACTOR_DAY_TO_HOUR 24                     // 天转小时倍率

#define FACTOR_YEAR_TO_SEC int64_t(365 * FACTOR_DAY_TO_SEC)       // 年转秒
#define FACTOR_YEAR_TO_MILLI int64_t(1000 * FACTOR_YEAR_TO_SEC)   // 年转毫秒
#define FACTOR_MONTH_TO_SEC int64_t(30 * FACTOR_DAY_TO_SEC)       // 月转秒
#define FACTOR_MONTH_TO_MILLI int64_t(1000 * FACTOR_MONTH_TO_SEC) // 月转毫秒
#define FACTOR_WEEK_TO_SEC int64_t(7 * FACTOR_DAY_TO_SEC)         // 周转秒
#define FACTOR_WEEK_TO_MILLI int64_t(7 * FACTOR_WEEK_TO_SEC)      // 周转毫秒

#define TIMESAPN_DEFAULT_PRECISION 1 // 时间值默认精度
#define TIMESAPN_DEFAULT_ROUND false // 是否按精度四舍五入

namespace cppc {

/// 表示时间段
class TimeSpan {
public:
    /// 时间单位，级别依次上升，精度依次下降
    enum Period {
        /// 毫秒 1/1000秒
        MilliSecond = 6,
        /// 秒
        Second = 11,
        /// 分 60秒
        Minute = 12,
        /// 小时 3600秒
        Hour = 13,
        /// 天 86400秒
        Day = 14,
        /// 周 604800秒
        Week = 15,
        /// 月 周与月相互转换比例为1:4
        Month = 22,
        /// 年
        Year = 23,
    };

    TimeSpan(double value = 1, Period period = MilliSecond, unsigned int precision = TIMESAPN_DEFAULT_PRECISION, bool round = TIMESAPN_DEFAULT_ROUND);

    TimeSpan(const TimeSpan &other);

    /// 根据时间描述字符串计算对应的数值，转换为毫秒
    /// \param timeStr 时间描述，支持小数，如0.01Y(2年)，0.2M(2月)，0.5D(2天)，2W(周)，2H(2小时)，2MIN(2分钟)，2S（2秒），无单位是毫秒
    TimeSpan(const std::string &timeStr, unsigned int precision = TIMESAPN_DEFAULT_PRECISION, bool round = TIMESAPN_DEFAULT_ROUND);

    ~TimeSpan();

    /// 格式化为字符串
    /// \return 时间描述，支持小数，如0.01Y(2年)，0.2M(2月)，0.5D(2天)，2W(周)，2H(2小时)，2MIN(2分钟)，2S（2秒），无单位是毫秒
    std::string toString() const;

    /// 克隆当前对象
    TimeSpan clone() const;

    /// 获取数值
    inline double value() const {
        return m_value;
    }

    /// 获取精度
    inline unsigned int precision() const {
        return m_precision;
    }

    inline bool round() const {
        return m_round;
    }

    /// 获取周期
    inline Period period() const {
        return m_period;
    }

    /// 设置时间段的数值和周期
    TimeSpan &set(double value, Period period = MilliSecond, unsigned int precision = TIMESAPN_DEFAULT_PRECISION, bool round = TIMESAPN_DEFAULT_ROUND);

    /// 设置时间段的数值
    TimeSpan &setValue(double value);

    /// 设置数值精度
    TimeSpan &setPrecision(unsigned int precision);

    /// 设置数值是否四舍五入
    TimeSpan &setRound(bool round);

    /// 设置时间段的周期
    TimeSpan &setPeriod(Period period);

    /// 提升级别，降低精度，这种转换方式并不是准确的，周与月转换时换1:4换算
    TimeSpan &rase();

    /// 降低级别，提升精度，这种转换方式并不是准确的，周与月转换时换1:4换算
    TimeSpan &down();

    /// 转换成指定类型的时间段
    TimeSpan &as(Period period);

    /// 获取值转换成某种类型后的值
    double valueAs(Period period) const;

public:
    TimeSpan operator+(const TimeSpan &other);
    TimeSpan operator+(double value);
    TimeSpan operator-(const TimeSpan &other);
    TimeSpan operator-(double value);
    TimeSpan &operator+=(const TimeSpan &other);
    TimeSpan &operator+=(double value);
    TimeSpan &operator-=(const TimeSpan &other);
    TimeSpan &operator-=(double value);
    bool operator>(const TimeSpan &other);
    bool operator>=(const TimeSpan &other);
    bool operator==(const TimeSpan &other);
    bool operator!=(const TimeSpan &other);
    bool operator<(const TimeSpan &other);
    bool operator<=(const TimeSpan &other);

protected:
    /// 使用精度更新值
    void update();

private:
    double m_value;
    unsigned int m_precision{TIMESAPN_DEFAULT_PRECISION}; ///< 精度，小数位个数
    bool m_round{TIMESAPN_DEFAULT_ROUND};                 ///< 在设置精度时，是否进行四舍五入
    Period m_period;                                      ///< 时间的单位
};
} // namespace cppc
